
#include <gtk/gtk.h>
#include <libglade-2.0/glade/glade.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sstream>


#include "../utility/assert_helper.h"
#include "../utility/charset_convert.h"
#include "../hive_access/nt_hive.h"
#include "../windows_manager/windows_info.h"
#include "../windows_manager/sys_search.h"
#include "../windows_manager/reg_accessor.h"
/*#include <glib/gi18n.h>
 
#define GETTEXT_PACKAGE "gtktests"
#define PACKAGE_LOCALE_DIR "./locale"*/
const char * DELIMITER = "\\";
void
some_handler(GtkWidget *widget)
{
    /* a handler referenced by the glade file.  Must not be static
     * so that it appears in the global symbol table. */
}

typedef std::vector<char> gstring;
enum
{
  COL_NAME = 0,
  COL_TYPE,
  COL_DATA,
  NUM_COLS
};

enum
{
  COLUMN = 0,
  COLS_NUM
};

extern const char *HKEY_ROOTS[HKEY_INVALID_ROOT];
	

GtkTreeModel *
create_and_fill_value_model (void)
{
  return GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT));
}

GtkTreeModel *
create_value_view_and_model (GtkWidget *view)
{
  GtkCellRenderer     *renderer;
  GtkTreeModel        *model;

  //view = gtk_tree_view_new ();

  /* --- Column #1 --- */
  renderer = gtk_cell_renderer_text_new ();
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
                                               -1,
                                               "Name",
                                               renderer,
                                               "text", COL_NAME,
                                               NULL);
  /* --- Column #2 --- */
  renderer = gtk_cell_renderer_text_new ();
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
                                               -1,
                                               "Type",
                                               renderer,
                                               "text", COL_TYPE,
                                               NULL);
  /* --- Column #3 --- */
  renderer = gtk_cell_renderer_text_new ();
  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
                                               -1,
                                               "Data",
                                               renderer,
                                               "text", COL_DATA,
                                               NULL);
  model = GTK_TREE_MODEL (gtk_list_store_new (NUM_COLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING));

  gtk_tree_view_set_model (GTK_TREE_VIEW (view), model);

  /* The tree view has acquired its own reference to the
   *  model, so we can drop ours. That way the model will
   *  be freed automatically when the tree view is destroyed */

  g_object_unref (model);

  return model;
}

GtkTreeModel *
key_model_initialize()
{
	GtkTreeIter	root_iter, default_child;
	GtkTreeStore *treestore = NULL;
	treestore = gtk_tree_store_new(COLS_NUM, G_TYPE_STRING);
	for (int i = 0; i < HKEY_INVALID_ROOT; ++i) {
		gtk_tree_store_append(treestore, &root_iter, NULL);
  		gtk_tree_store_set(treestore, &root_iter, COLUMN, HKEY_ROOTS[i], -1);
  		
		gtk_tree_store_append(treestore, &default_child, &root_iter);
		gtk_tree_store_set(treestore, &default_child, COLUMN, "\\", -1);
	}
	return GTK_TREE_MODEL(treestore);
}

GtkWidget *
create_key_view_and_model (GtkWidget *view)
{
  GtkTreeViewColumn *col;
  GtkCellRenderer *renderer;
  GtkTreeModel *model;

  IF_NULL_RETURN_VALUE(view, NULL);

  col = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(col, "Register key");
  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);

  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_column_pack_start(col, renderer, TRUE);
  gtk_tree_view_column_add_attribute(col, renderer,
      "text", COLUMN);

  model = key_model_initialize();
  gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
  g_object_unref(model);

  return (GtkWidget *)model;
}

struct	CB_Row_Expand_Data {
	CB_Row_Expand_Data(GtkWidget * tmodel = NULL,
	GtkWidget * lmode = NULL,
	GtkWidget * stbar = NULL, 
	Windows_Information * win_info = NULL, 
	guint	id = 0,
	GtkTreeIter * iter = NULL) :
	tree_model_(tmodel), list_store_(lmode), status_bar_(stbar), 
	win_info_(win_info), select_iter_(iter), id_(id) {}
	bool is_available() { return (tree_model_ && status_bar_ && win_info_);}
	GtkWidget	* tree_model_;
	GtkWidget * list_store_;
	GtkWidget	* status_bar_;
	Windows_Information * win_info_;
	GtkTreeIter	* select_iter_;	
	guint id_;
	std::string current_path_;
};


void
travel_path_for_show_all_subkeys(const char *path, struct nk_key * key_data, void *userdata)
{
	g_return_if_fail(NULL != path && NULL != key_data && NULL != userdata);
	CB_Row_Expand_Data *data = (CB_Row_Expand_Data *)userdata;
	GtkTreeIter		child, grandson;

	gtk_tree_store_append((GtkTreeStore*)(data->tree_model_), &child, data->select_iter_);
	gtk_tree_store_set((GtkTreeStore*)(data->tree_model_), &child, COLUMN, path, -1);
	if (0 < key_data->no_subkeys) {
		gtk_tree_store_append((GtkTreeStore*)(data->tree_model_), &grandson, &child);
		gtk_tree_store_set((GtkTreeStore*)(data->tree_model_), &grandson, COLUMN, "\\", -1);
	}
}

bool
reg_relation_analyzer(const std::string & whole_path, std::string & root, std::string & subkey)
{
	if (!whole_path.empty()) {
		size_t delimiter = whole_path.find_first_of("\\");
		if (std::string::npos != delimiter) {
			root = whole_path.substr(0, delimiter);
			subkey = whole_path.substr(delimiter + 1);
			return true;
		}
	}
	return false;
}

std::string	take_whole_key_path(GtkTreeModel	*tree_model, GtkTreeIter *iter)
{
	g_return_val_if_fail(NULL != tree_model && NULL != iter, "");
	gchar * level = 
	gtk_tree_model_get_string_from_iter(tree_model, iter);
	g_return_val_if_fail(NULL != level, "");
	std::string key_path;
	std::string path_str = level;
	gchar * cname = NULL;
	size_t delimiter = std::string::npos;
	if (NULL != cname) key_path = cname;
	do {
			path_str = path_str.substr(0, delimiter);
			gtk_tree_model_get_iter_from_string(tree_model, iter, path_str.c_str());
			gtk_tree_model_get(tree_model, iter, 0, &cname, -1);
			//gtk_tree_model_get((GtkTreeModel *)data->tree_model_, &child, 0, &cname, -1);
			if (NULL != cname && strlen(cname) > 0) {
				// 通过判断去除最后的\符
				if (!key_path.empty()) {
					key_path = cname + std::string("\\") + key_path;
				} else {
					key_path += cname;
				}					
				g_free(cname);
			}
			delimiter = path_str.find_last_of(":");
		} while (std::string::npos != delimiter);
	//g_print("[%s]\n", key_path.c_str());
	g_free(level);
	return key_path;
}

// 树展开回调函数
void	view_on_row_expanded(GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path,
gpointer userdata)
{
	g_return_if_fail(NULL != treeview && NULL != iter && NULL != path && NULL != userdata);
	GtkTreeIter	child, grandson;
	CB_Row_Expand_Data *data = (CB_Row_Expand_Data *)userdata;
	Hive_Accessor reg(data->win_info_);
	
	if (data->is_available())
	{
		gchar * level = 
		gtk_tree_model_get_string_from_iter((GtkTreeModel *)data->tree_model_, iter);		
		
		g_print("current path string: %s\n", level);
		g_return_if_fail(NULL != level);
		if (NULL == strstr(level, ":")) { // is root key
			int index = atoi(level);
			// 判断是否有子键
			if (gtk_tree_model_iter_has_child((GtkTreeModel *)data->tree_model_, iter)) {
				gchar * first_child_name = NULL;
				// 获取第一个子键
				gtk_tree_model_iter_children((GtkTreeModel *)data->tree_model_, &child, iter);
				// 获取该键名称
				gtk_tree_model_get((GtkTreeModel *)data->tree_model_, &child, 0, &first_child_name, -1);
				if (0 == strcmp(first_child_name, "\\")) {
					g_print("%s\n", first_child_name);
					g_free(first_child_name);
				} else {	
					g_free(first_child_name);
							
					return;					
				}
			}

			if (index >= 0 && index < HKEY_INVALID_ROOT) {
				const Hive_Root & hroot = reg.take_root(static_cast<HKEY>(index));
				Hive_Root::Link_Relations::const_iterator end = hroot.link_relations().end();
				for (Hive_Root::Link_Relations::const_iterator begin = hroot.link_relations().begin();
				begin != end; ++begin) {
					g_print("%s->%s.\n", begin->key_name().c_str(), begin->target().c_str());
					// 增加子键
					if (!begin->key_name().empty()) // 命名子键
					{
						gtk_tree_store_append((GtkTreeStore*)(data->tree_model_), &child, iter);
						gtk_tree_store_set((GtkTreeStore *)data->tree_model_, &child, COLUMN, 
						begin->key_name().c_str(), -1);
						gtk_tree_store_append((GtkTreeStore *)data->tree_model_, &grandson, &child);
						gtk_tree_store_set((GtkTreeStore *)data->tree_model_, &grandson, COLUMN, "\\", -1);
					} else {  //匿名子键（映射键）
						size_t delimiter = begin->target().find_first_of("\\");
						if (std::string::npos != delimiter) {
							reg.reg_open(begin->target().substr(0, delimiter).c_str(),
							begin->target().substr(delimiter + 1).c_str());
							if (reg.is_available()) {
								data->select_iter_ = iter;
								reg.subkey_foreach(".", travel_path_for_show_all_subkeys, data);
							}							
						} else {
							g_print("%s may be a invalid key name\n", begin->target().c_str());
						}
					}
				}
				gtk_tree_model_iter_nth_child((GtkTreeModel *)data->tree_model_, &child, iter, 0);
				gtk_tree_store_remove((GtkTreeStore *)data->tree_model_, &child); // 删除首子键（\）
			}
		} else { // a child key
			std::string key_path;
			std::string path_str = level;
			gchar * cname = NULL;
			size_t delimiter = std::string::npos;
			if (NULL != cname) key_path = cname;
			do {
					path_str = path_str.substr(0, delimiter);
					gtk_tree_model_get_iter_from_string((GtkTreeModel *)data->tree_model_,
					&child, path_str.c_str());
					gtk_tree_model_get((GtkTreeModel *)data->tree_model_, &child, 0, &cname, -1);
					//g_print("=%s\t",cname);
					// 通过判断去除最后的\符
					if (!key_path.empty()) {
						key_path = cname + std::string("\\") + key_path;
					} else {
						key_path += cname;
					}					
					g_free(cname);
					delimiter = path_str.find_last_of(":");
				} while (std::string::npos != delimiter);
			GtkTreeIter tmp_iter;
			if (gtk_tree_model_iter_children((GtkTreeModel *)data->tree_model_, &tmp_iter, iter)) {
				gtk_tree_model_get((GtkTreeModel *)data->tree_model_, &tmp_iter, 0, &cname, -1);
				g_print("son: %s\n", cname);
				if ('\\' == *cname && 1 == strlen(cname)) { // 第一次展开子键
					//gtk_tree_model_iter_nth_child((GtkTreeModel *)data->tree_model_, &child, &tmp_iter, 0);
					size_t delimiter = key_path.find_first_of("\\");
					if (std::string::npos != delimiter) {
						reg.reg_open(key_path.substr(0, delimiter).c_str(),
						key_path.substr(delimiter + 1).c_str());
						g_print(">%s\n", key_path.c_str());
						if (reg.is_available()) {							
							data->select_iter_ = iter;
							reg.subkey_foreach(".", travel_path_for_show_all_subkeys, data);
							gtk_tree_store_remove((GtkTreeStore *)data->tree_model_, &tmp_iter); // 删除首子键（\）
						}
					}
				}
				g_free(cname);
			}			
		}
		g_free(level);
		/*gchar * key_path = NULL;
		gtk_tree_model_get(model, &iter, 0, &key_path, -1);
		g_print("key path:%s\n", key_path);
		g_free(key_path);*/
	}
	//gtk_tree_model_row_deleted(model, path);
}

const char * get_type_string(unsigned type)
{
	const char * s_type_str[REG_MAX] = {"REG_NONE", "REG_SZ", "REG_EXPAND_SZ", "REG_BINARY", "REG_DWORD", 
	"REG_DWORD_BIG_ENDIAN", "REG_LINK", "REG_MULTI_SZ", "REG_RESOURCE_LIST", 
	"REG_FULL_RESOURCE_DESCRIPTOR", "REG_RESOURCE_REQUIREMENTS_LIST", "REG_QWORD"};
	return (type > REG_NONE && type < REG_MAX) ? s_type_str[type] : s_type_str[REG_NONE];
}

char *_(char *c)
{
    return(g_locale_to_utf8(c,-1,0,0,0));
}

std::string
get_data_in_string(const Hive_Accessor *reg, const std::string & name)
{
	g_return_val_if_fail(NULL != reg, "");
	int	type = REG_NONE;
	std::stringstream	result;
	char	*buf = NULL;
	size_t len = reg->get_value_length(name.c_str());
	if (0 < len) {
		buf = new char[len];
		if (NULL != buf && 
		0 == reg->get_value(name.c_str(), buf, len, type)) {
			switch (type) {
			case REG_DWORD:
				result << *(reinterpret_cast<int *>(buf));
				break;
			case REG_SZ:
				char str[1024] = {0x0};
				utility::TCHAR us[1024] = {0};
				memset(str, 0, sizeof(str));
				utility::UnicodeToUTF_8(str, 1023, reinterpret_cast<utility::TCHAR *>(buf));
				//utility::UTF_8ToUnicode(us, 2047, str);
				//memset(str, 0, sizeof(str));
				//utility::UnicodeToUTF_8(str, 1023, us);
				//printf("%4x%4x\n", us[0], us[1]);
				result << str;
				//for (int i = 0; i < len; i += 2) {
					//memset(str, 0, sizeof(str));
					//utility:: UnicodeToUTF_8(str, reinterpret_cast<utility::TCHAR *>(buf + i));
					//printf("%x%x%x\n", (unsigned char)(str[0]),  (unsigned char)(str[1]),  
					//(unsigned char)(str[2]));
					//result << str/*_(reinterpret_cast<char *>(buf))*/;
				//}
				/*printf("%x%x%x%x%x%x%x\n", (unsigned char)(str[0]),  (unsigned char)(str[1]),  
				(unsigned char)(str[2]),  (unsigned char)(str[3]),  (unsigned char)(str[4]),  
				(unsigned char)(str[5]), (unsigned char)(str[6]));*/
				break;
			}
			delete [] buf;
		}
	}

	return result.str();
}

void
travel_key_for_show_all_values(const char * name, struct vk_key * val_data, void * userdata)
{
	g_return_if_fail(NULL != name && NULL != val_data && NULL != userdata);
	CB_Row_Expand_Data *data = (CB_Row_Expand_Data *)userdata;
	GtkListStore * list = (GtkListStore*)(data->list_store_);
	GtkTreeIter iter;
	Hive_Accessor reg(data->win_info_);
	std::string root, sub;
	reg_relation_analyzer(data->current_path_, root, sub);
	reg.reg_open(root.c_str(), sub.c_str());
	if (NULL != list) {		
		gtk_list_store_append(list, &iter);
		gtk_list_store_set(list, &iter,
							 COL_NAME, name,
							 COL_TYPE, get_type_string(val_data->val_type),
							 COL_DATA, get_data_in_string(&reg, name).c_str(),
							 -1);
	}
}

void
tree_selection_changed_cb(GtkTreeSelection *selection, gpointer userdata)
{
	GtkTreeIter iter;
	GtkTreeModel *model = NULL;
	std::string path;
	std::string	root;
	std::string	subkey;
	CB_Row_Expand_Data *data = (CB_Row_Expand_Data *)userdata;
	g_return_if_fail(NULL != data);
	GtkListStore * list = (GtkListStore*)(data->list_store_);
	Hive_Accessor reg(data->win_info_);
	
	if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
		if (NULL != model) path = take_whole_key_path(model, &iter);
		if (reg_relation_analyzer(path, root, subkey)) {
			if (0 == reg.reg_open(root.c_str(), subkey.c_str())) {
				if (NULL != list) gtk_list_store_clear(list);
				data->current_path_ = path;
				reg.value_foreach(".", travel_key_for_show_all_values, data);
			}
		}
		gtk_statusbar_pop((GtkStatusbar*)(data->status_bar_), data->id_);
		gtk_statusbar_push((GtkStatusbar*)(data->status_bar_), data->id_, path.c_str());
	}
}

gstring	regsz_editor(const GtkWidget * parent, gchar *name, gchar *data)
{
	GladeXML *dialog_xml = NULL;
	GtkWidget *dialog = NULL;
	GtkWidget *val_name = NULL;
	GtkWidget *val_data = NULL;
	GtkWidget *bok = NULL;
	GtkWidget *bcancle = NULL;
	IF_NULL_RETURN_VALUE(name, gstring('\0'));
	IF_NULL_RETURN_VALUE(data, gstring('\0'));
	dialog_xml = glade_xml_new("./ui.glade", NULL, NULL);
	if(!dialog_xml) {
		GtkWidget *dialog = gtk_message_dialog_new ((GtkWindow *)parent,
									 GTK_DIALOG_DESTROY_WITH_PARENT,
									 GTK_MESSAGE_ERROR,
									 GTK_BUTTONS_CLOSE,
									 "We could not load the interface resource!");
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);
		return gstring('\0');
	}
	dialog = glade_xml_get_widget(dialog_xml, "regsz_editor");
	val_name = glade_xml_get_widget(dialog_xml, "name");
	val_data = glade_xml_get_widget(dialog_xml, "data");
	bok = glade_xml_get_widget(dialog_xml, "ok");
	bcancle = glade_xml_get_widget(dialog_xml, "cancle");
	gtk_entry_set_text((GtkEntry *)val_name, name);
	gtk_entry_set_text((GtkEntry *)val_data, data);
	glade_xml_signal_autoconnect(dialog_xml);
	gtk_widget_show_all(dialog);
	return gstring('\0');
}

int
main(int argc, char **argv)
{
	/*bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
	bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF8");
	textdomain (GETTEXT_PACKAGE);*/
	GladeXML *xml = NULL;
	GtkWidget *form = NULL;
	GtkWidget *key_view = NULL;
	GtkWidget *value_view = NULL;
	GtkWidget *status_bar = NULL;
	
	gtk_init(&argc, &argv);
	xml = glade_xml_new("./ui.glade", NULL, NULL);
	if(!xml) {
		GtkWidget *dialog = gtk_message_dialog_new ((GtkWindow *)form,
									 GTK_DIALOG_DESTROY_WITH_PARENT,
									 GTK_MESSAGE_ERROR,
									 GTK_BUTTONS_CLOSE,
									 "We could not load the interface resource!");
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);
		return -1;
	}
	
	/* get a widget (useful if you want to change something) */
	form = glade_xml_get_widget(xml, "main_form");
	value_view = glade_xml_get_widget(xml, "treeview2");
	key_view = glade_xml_get_widget(xml, "treeview1");
	status_bar = glade_xml_get_widget(xml, "statusbar1");
	int contex_id = gtk_statusbar_get_context_id((GtkStatusbar *)status_bar, "registry key path");
	//status_bar_content sbc(contex_id, (GtkStatusbar *)status_bar);
//regsz_editor(form);
	GtkWidget *val_model = GTK_WIDGET(create_value_view_and_model (value_view));
	GtkWidget *key_model = create_key_view_and_model(key_view);

	Windows_Info_Group & win_infos = windows_searcher::get_information_of_windows();
	if (0 == win_infos.get_windows_count()) {
		GtkWidget *dialog = gtk_message_dialog_new ((GtkWindow *)form,
											 GTK_DIALOG_DESTROY_WITH_PARENT,
											 GTK_MESSAGE_ERROR,
											 GTK_BUTTONS_CLOSE,
											 "Can't found windows my lord");
		gtk_dialog_run (GTK_DIALOG (dialog));
		gtk_widget_destroy (dialog);
		return -1;
	}
	/* connect signal handlers */
	glade_xml_signal_autoconnect(xml);
	CB_Row_Expand_Data cb_expand(key_model, val_model, status_bar, 
	win_infos.get_specified_windows_information(0), contex_id);
	g_signal_connect(key_view, "row-expanded", (GCallback)view_on_row_expanded, &cb_expand);
	GtkTreeSelection * selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(key_view));
	g_signal_connect (G_OBJECT(selection), "changed", G_CALLBACK(tree_selection_changed_cb), &cb_expand);
	gtk_widget_show_all(form);
	gtk_object_unref(GTK_OBJECT(xml)); 
	gtk_main();
	return 0;		
}
